package com.lyw.GeoLocationClient.ui;

import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.baidu.location.BDLocation;
import com.baidu.mapapi.map.*;
import com.baidu.mapapi.model.LatLng;
import com.baidu.mapapi.navi.BaiduMapAppNotSupportNaviException;
import com.baidu.mapapi.navi.BaiduMapNavigation;
import com.baidu.mapapi.navi.NaviParaOption;
import com.baidu.mapapi.overlayutil.DrivingRouteOverlay;
import com.baidu.mapapi.search.core.SearchResult;
import com.baidu.mapapi.search.route.*;
import com.easemob.applib.utils.HXPreferenceUtils;
import com.easemob.chatui.activity.ChatActivity;
import com.lyw.GeoLocationClient.R;
import com.lyw.GeoLocationClient.service.LBSClient;
import com.lyw.GeoLocationClient.service.LBSLocation;
import com.lyw.GeoLocationClient.ui.map.IconGenerator;
import timber.log.Timber;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;

public class BaiduMapUtils {

    private final ViewGroup navView;
    private Context context;
    private final BaiduMap mBaiduMap;
    private RoutePlanSearch mSearch;
    private RouteLinePlayer mRoutePlayer;
    private HashMap<Marker, List<LatLng>> markerRouteMap = new HashMap<Marker, List<LatLng>>();
    View infoView;
    private OnMarkerClickListener onMarkerClickListener;
    private HashMap<String, List<Marker>> markerMap = new HashMap<String, List<Marker>>();

    public void setOnMarkerClickListener(OnMarkerClickListener onMarkerClickListener) {
        this.onMarkerClickListener = onMarkerClickListener;
    }

    public interface OnMarkerClickListener{
        void onMarkClick(Marker marker, String name);
    }

    public BaiduMapUtils(final Context context, BaiduMap map) {
        this.context = context;
        this.mBaiduMap = map;

        mSearch = RoutePlanSearch.newInstance();
        mRoutePlayer = new RouteLinePlayer(mBaiduMap, null);
//        mSearch.setOnGetRoutePlanResultListener(onGetRoutePlanResultListener);
        iconFactory = new IconGenerator(context);


        mBaiduMap.setOnMarkerClickListener(new BaiduMap.OnMarkerClickListener() {
            @Override
            public boolean onMarkerClick(Marker marker) {
                Timber.d("onMarkerClick " + marker.toString());

                if (!mRoutePlayer.isPlaying() && marker.getExtraInfo() != null) {
                    final String username = marker.getExtraInfo().getString("user");
                    InfoWindow currentInfoWindow =
                            new InfoWindow(getInfoWindowView(username, infoView, marker), marker.getPosition(), -127);

                    mBaiduMap.showInfoWindow(currentInfoWindow);

                    if (onMarkerClickListener != null) {
                        onMarkerClickListener.onMarkClick(marker, username);
                    }
                }

                return false;
            }

        });

        mBaiduMap.setOnMapLongClickListener(new BaiduMap.OnMapLongClickListener() {
            @Override
            public void onMapLongClick(LatLng latLng) {
                InfoWindow currentInfoWindow =
                        new InfoWindow(navView, latLng, -10);
                navView.setTag(latLng);
                mBaiduMap.showInfoWindow(currentInfoWindow);
            }
        });

        dis = HXPreferenceUtils.getInstance().getOptimizeSize();

        LayoutInflater inflater = LayoutInflater.from(context);
        infoView = inflater.inflate(R.layout.marker_info_window, null);
        navView = (ViewGroup) inflater.inflate(R.layout.nav_window, null);
        navView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                startNav(v);
                mBaiduMap.hideInfoWindow();
            }
        });
    }

    private void startNav(View v) {
        LatLng dstLatLng = (LatLng) v.getTag();
        BDLocation location = LBSClient.getInstance().getCurrLocation();
        if (location == null) {
            return;
        }
        LatLng cur = new LatLng(location.getLatitude(), location.getLongitude());

        NaviParaOption naviParaOption = new NaviParaOption();
        naviParaOption.startPoint(cur);
        naviParaOption.endPoint(dstLatLng);

        try {

            BaiduMapNavigation.openBaiduMapNavi(naviParaOption, context);

        } catch (BaiduMapAppNotSupportNaviException e) {
            e.printStackTrace();
            AlertDialog.Builder builder = new AlertDialog.Builder(context);
            builder.setMessage("您尚未安装百度地图app或app版本过低，点击确认安装？");
            builder.setTitle("提示");
            builder.setPositiveButton("确认", new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {
                    dialog.dismiss();
                }
            });

            builder.setNegativeButton("取消", new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {
                    dialog.dismiss();
                }
            });

            builder.create().show();
        }
    }

    OnGetRoutePlanResultListener onGetRoutePlanResultListener = new OnGetRoutePlanResultListener() {
        @Override
        public void onGetWalkingRouteResult(WalkingRouteResult result) {
        }

        @Override
        public void onGetTransitRouteResult(TransitRouteResult transitRouteResult) {
        }

        @Override
        public void onGetDrivingRouteResult(DrivingRouteResult result) {
            if (result == null || result.error != SearchResult.ERRORNO.NO_ERROR) {
                Timber.e("get route error");
                return;
            }
            Timber.d("get route error  " + result.error);
            if (result.error == SearchResult.ERRORNO.AMBIGUOUS_ROURE_ADDR) {
                return;
            }

            DrivingRouteOverlay routeOverlay = new DrivingRouteOverlay(mBaiduMap);
            routeOverlay.setData(result.getRouteLines().get(0));
            routeOverlay.addToMap();

        }

        @Override
        public void onGetBikingRouteResult(BikingRouteResult bikingRouteResult) {

        }
    };

    private View getInfoWindowView(final String username, View infoView, final Marker marker){
        infoView.findViewById(R.id.info_send_msg).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Timber.d("click send msg");
                ChatActivity.startChatActivity(context, username, ChatActivity.CHATTYPE_SINGLE);
                mBaiduMap.hideInfoWindow();
            }
        });

        infoView.findViewById(R.id.info_play_route).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Timber.d("click play_route " + marker);

                List<LatLng> list = markerRouteMap.get(marker);
                if (list != null && list.size() > 0) {
                    mRoutePlayer.play(list);
                    mBaiduMap.hideInfoWindow();
                } else {
                    Timber.e("get marker route failed");
                }
            }
        });
        return  infoView;
    }

    public RouteLinePlayer getRoutePlayer() {
        return mRoutePlayer;
    }

    public void animateUserStatus() {
        BDLocation location = LBSClient.getInstance().getCurrLocation();
        if (location != null) {
            LatLng ll = new LatLng(location.getLatitude(),
                    location.getLongitude());
            MapStatusUpdate u = MapStatusUpdateFactory.newLatLngZoom(ll, 17.0f);
            mBaiduMap.animateMapStatus(u);

            MyLocationData locData = new MyLocationData.Builder()
                    .accuracy(location.getRadius())
                    .direction(location.getDirection()).latitude(location.getLatitude())
                    .longitude(location.getLongitude()).build();
            mBaiduMap.setMyLocationData(locData);
        }
    }

    public void drawGeofanceOnMap(LinkedList<LatLng> geofanceList, Stroke stroke, int fillColor) {
        if (geofanceList.size() > 2) {
            OverlayOptions ooPolygon = new PolygonOptions().points(geofanceList)
                    .stroke(stroke)
                    .fillColor(fillColor);
            mBaiduMap.addOverlay(ooPolygon);
        }
    }

    public void drawRoute(List<LatLng> pts) {

        if (pts.size() > 1) {

            DrivingRoutePlanOption drivingRoutePlanOption = new DrivingRoutePlanOption();
            List<PlanNode> nodes = new ArrayList<PlanNode>();
            for (int j = pts.size() -1; j > 0; j--) {
                if (j == pts.size() - 1) {
                    drivingRoutePlanOption.from(PlanNode.withLocation(pts.get(j)));
                    continue;
                }
                if (j - 1 == 0) {
                    drivingRoutePlanOption.to(PlanNode.withLocation(pts.get(0)));
                    break;
                }
                PlanNode stNode = PlanNode.withLocation(pts.get(j));
                nodes.add(stNode);
            }
            drivingRoutePlanOption.passBy(nodes);
            mSearch.drivingSearch(drivingRoutePlanOption);

        }
    }

    public void setRouteLinePlayerCallback(RouteLinePlayer.RouteLinePlayerCallback routeLinePlayerCallback) {
        mRoutePlayer.setCallBack(routeLinePlayerCallback);
    }


    private HashMap<String, BitmapDescriptor> iconMap = new HashMap<String, BitmapDescriptor>();
    IconGenerator iconFactory;

    public void setItemIcon(String name, String username, List<LatLng> pts) {
        if (pts == null || pts.size() == 0) {
            return;
        }

        LatLng potion = pts.get(pts.size() - 1);

        BitmapDescriptor icon = null;
        icon = iconMap.get(name);
        if (icon == null) {
            icon = BitmapDescriptorFactory.fromBitmap(iconFactory.makeIcon(name));
            iconMap.put(name, icon);
        }

        OverlayOptions option = new MarkerOptions()
                .position(potion)
                .icon(icon);
        Marker marker = (Marker) mBaiduMap.addOverlay(option);
        Bundle bundle = new Bundle();
        bundle.putString("user", username);
        marker.setExtraInfo(bundle);

        markerRouteMap.put(marker, pts);
        Timber.d("put marker " + marker + " size " + pts.size());

        if (markerMap.get(username) == null) {
            markerMap.put(username, new ArrayList<Marker>());
        }
        markerMap.get(username).add(marker);
    }

    public void clearMarker(String username) {
        List<Marker> list = markerMap.get(username);
        if (list != null) {
            for (Marker marker : list) {
                marker.remove();
            }
        }
    }

    public Overlay drawTrackOverlay(GeotableItem geotableItem, List<LatLng> list, int color) {
        //使用路线规划功能描绘轨迹
//            mapUtils.drawRoute(pts);
        //抽希算法
        List<LatLng> optimizePoints = BaiduMapUtils.optimizePoints(list);
        //道格拉斯算法
//            List<LatLng> optimizePoints = mapUtils.douglasPoints(pts);

        Overlay overlay = null;

        if (optimizePoints.size() > 2 && optimizePoints.size() < 10000) {
            PolylineOptions options = new PolylineOptions().color(context.getResources().getColor(color)).width(6)
                    .points(optimizePoints);
            overlay = mBaiduMap.addOverlay(options);

            Bundle bundle = new Bundle();
            bundle.putString("user", geotableItem.getName());
            overlay.setExtraInfo(bundle);

            setItemIcon(geotableItem.getTitle(), geotableItem.getName(), optimizePoints);
        }

        return overlay;
    }

    public void clearOldData() {
        markerRouteMap.clear();
    }

    public void drawTime(LatLng llText, String time) {
        OverlayOptions textOption = new TextOptions()
                .bgColor(0xAAFFFF00)
                .fontSize(30)
                .fontColor(0xFFFF00FF)
                .text(time)
                .rotate(0)
                .position(llText);
        mBaiduMap.addOverlay(textOption);
    }

    static int dis = 100;
    static final double degToMeter = Math.PI * 6378137 / 180.0;//6378137赤道半径，一度对应赤道上的一米
    static final int buf = (int) (dis * 1.0e7 / degToMeter);//1公里对应多少度

    /**
     * 点抽稀算法
     * @param inPoint
     * @return
     */
    public static List<LatLng> optimizePoints(List<LatLng> inPoint, int distance) {
        int buf = (int) (distance * 1.0e7 / degToMeter);

        List<LatLng> out = new ArrayList<LatLng>();
        List<Pos> spector = new LinkedList<Pos>();

        for (int i = 0; i < inPoint.size(); i++) {
            LatLng latLng = inPoint.get(i);
            int x = (int) (latLng.latitude * 1e7);
            int y = (int) (latLng.longitude * 1e7);

            Pos cur = new Pos(x, y);
            cur.setBuffer(buf);
            if (!spector.contains(cur)) {
                spector.add(cur);
                out.add(latLng);
            }
        }

        return out;
    }

    public static List<LatLng> optimizePoints(List<LatLng> inPoint) {
        return optimizePoints(inPoint, dis);
    }

    /**
     * 道格拉斯-普克抽稀算法
     * @param inPoint
     * @return
     */
    public List<LatLng> douglasPoints(List<LatLng> inPoint) {
        List<Point> spector = new LinkedList<Point>();

        for (int i = 0; i < inPoint.size(); i++) {
            LatLng latLng = inPoint.get(i);
            Point cur = new Point(latLng.latitude, latLng.longitude, i);
            spector.add(cur);
        }


        compress(spector, spector.get(0), spector.get(spector.size() - 1));

        List<LatLng> out = new ArrayList<LatLng>();
        for (Point point : spector) {
            if (point.getIndex() > -1) {
                LatLng latLng = new LatLng(point.getX(), point.getY());
                out.add(latLng);
            }
        }
        return out;
    }

    /**
     * 控制数据压缩精度的极差
     */
    private static final double D = 1;

    /**
     * 对矢量曲线进行压缩
     *
     * @param from
     *            曲线的起始点
     * @param to
     *            曲线的终止点
     */
    public void compress(List<Point> points, Point from, Point to) {

        /**
         * 压缩算法的开关量
         */
        boolean switchvalue = false;

        /**
         * 由起始点和终止点构成的直线方程一般式的系数
         */

        double A = (from.getY() - to.getY())
                / Math.sqrt(Math.pow((from.getY() - to.getY()), 2)
                + Math.pow((from.getX() - to.getX()), 2));

        /**
         * 由起始点和终止点构成的直线方程一般式的系数
         */
        double B = (to.getX() - from.getX())
                / Math.sqrt(Math.pow((from.getY() - to.getY()), 2)
                + Math.pow((from.getX() - to.getX()), 2));

        /**
         * 由起始点和终止点构成的直线方程一般式的系数
         */
        double C = (from.getX() * to.getY() - to.getX() * from.getY())
                / Math.sqrt(Math.pow((from.getY() - to.getY()), 2)
                + Math.pow((from.getX() - to.getX()), 2));

        double d = 0;
        double dmax = 0;
        int m = points.indexOf(from);
        int n = points.indexOf(to);
        if (n == m + 1)
            return;
        Point middle = null;
        List<Double> distance = new ArrayList<Double>();
        for (int i = m + 1; i < n; i++) {
            d = Math.abs(A * (points.get(i).getX()) + B
                    * (points.get(i).getY()) + C)
                    / Math.sqrt(Math.pow(A, 2) + Math.pow(B, 2));
            distance.add(d);
        }
        dmax = distance.get(0);
        for (int j = 1; j < distance.size(); j++) {
            if (distance.get(j) > dmax)
                dmax = distance.get(j);
        }
        switchvalue = dmax > D;
        if (!switchvalue) {
            // 删除Points(m,n)内的坐标
            for (int i = m + 1; i < n; i++) {
                points.get(i).setIndex(-1);
            }

        } else {
            for (int i = m + 1; i < n; i++) {
                if ((Math.abs(A * (points.get(i).getX()) + B
                        * (points.get(i).getY()) + C)
                        / Math.sqrt(Math.pow(A, 2) + Math.pow(B, 2)) == dmax))
                    middle = points.get(i);
            }
            compress(points, from, middle);
            compress(points, middle, to);
        }
    }

    public class Point {
        /**
         * 点的X坐标
         */
        private double x = 0;

        /**
         * 点的Y坐标
         */
        private double y = 0;

        /**
         * 点所属的曲线的索引
         */
        private int index = 0;

        public double getX() {
            return x;
        }

        public void setX(double x) {
            this.x = x;
        }

        public double getY() {
            return y;
        }

        public void setY(double y) {
            this.y = y;
        }

        public int getIndex() {
            return index;
        }

        public void setIndex(int index) {
            this.index = index;
        }

        /**
         * 点数据的构造方法
         *
         * @param x
         *            点的X坐标
         * @param y
         *            点的Y坐标
         * @param index 点所属的曲线的索引
         */
        public Point(double x, double y, int index) {
            this.x = x;
            this.y = y;
            this.index = index;
        }
    }


}
